//
//  Event.Core.swift
//  Utility
//
//  Created by 何海涛 on 2017/5/25.
//  Copyright © 2017年 何海涛. All rights reserved.
//

import Foundation

// MARK: - IWeakableContainer

/// 可以同时维护`强引用`与`弱引用`的容器
public protocol IWeakableContainer {
    
    associatedtype Element: AnyObject
    
    /// 有效元素个数
    var count: Int { get }
    
    
    /// 获取所有有效元素迭代器
    ///
    /// - Returns: 所有有效元素迭代器
    func getIterator() -> IndexingIterator<[Element]>
    
    /// 判断容器中是否存在元素
    ///
    /// - Parameter element: 元素
    /// - Returns: 是否存在
    func contains(element: Element) -> Bool
    
    /// 添加强引用
    ///
    /// - Parameter element: 元素
    /// - Returns: `Self`新增一个强引用元素
    mutating func add(element: Element)
    
    /// 添加弱引用
    ///
    /// - Parameter element: 元素
    /// - Returns: `Self`新增一个弱引用元素
    mutating func addWeak(element: Element)
    
    /// 移除元素
    ///
    /// - Parameter element: 元素
    /// - Returns: `Self`移除引用值相同的元素
    mutating func remove(element: Element)
    
    /// 移除元素
    ///
    /// - Parameter predicate: 移除条件
    /// - Returns: `Self`移除满足条件的元素
    mutating func remove(where predicate: (Element)->Bool)
    
    /// 移除所有元素
    mutating func removeAll()
    
    /// 垃圾清除
    /// - Note: 因为包含弱引用，所以有必要提供这个方法来做一些清理工作
    mutating func gc()
}

/// 可以同时维护`强引用`与`弱引用`的容器
public struct WeakableContainer<T: AnyObject>: IWeakableContainer {
    
    /// 一个可以存储`强引用`或者`弱引用`的包装器
    fileprivate class StrongWeakStorage<T: AnyObject> {
        private var strong: T?
        private weak var `weak`: T?
        private var isWeak: Bool
        init(raw: T, weak: Bool) {
            isWeak = weak
            if isWeak {self.weak = raw }
            else { strong = raw }
        }
        
        var raw: T? { return isWeak ? self.weak : self.strong }
    }
    
    public typealias Element = T

    fileprivate var container: Array<StrongWeakStorage<T>> = []
    
    public /// 有效元素数量。强引用数量+有效弱引用数量
    var count: Int {
        return container.filter({ $0.raw != nil }).count
    }
    
    public init() { }
    
    public /// 获取所有有效元素迭代器
    ///
    /// - Returns: 所有有效元素迭代器
    func getIterator() -> IndexingIterator<[T]> {
        // 通过拷贝副本来提高线程安全性
        // 防止`add`／`remove`之类的操作并发
        let copy = container.filter({ $0.raw != nil }).map({ $0.raw! })
        return copy.makeIterator()
    }
    
    public /// 判断容器中是否存在元素
    ///
    /// - Parameter element: 元素
    /// - Returns: 是否存在
    func contains(element: T) -> Bool {
        return container.contains(where: { $0.raw === element })
    }
    
    public /// 添加强引用
    ///
    /// - Parameter element: 元素
    /// - Returns: `Self`新增一个强引用元素
    mutating func add(element: T) {
        container.append(StrongWeakStorage<T>.init(raw: element, weak: false))
    }
    
    public /// 添加弱引用
    ///
    /// - Parameter element: 元素
    /// - Returns: `Self`新增一个弱引用元素
    mutating func addWeak(element: T) {
        container.append(StrongWeakStorage<T>.init(raw: element, weak: true))
    }
    
    public /// 移除元素
    ///
    /// - Parameter element: 元素
    /// - Returns: `Self`移除引用值相同的元素
    mutating func remove(element: T) {
        remove(where: { $0 === element })
    }
    
    public /// 移除元素
    ///
    /// - Parameter predicate: 移除条件
    /// - Returns: `Self`移除满足条件的元素
    mutating func remove(where predicate: (T) -> Bool) {
        while let index = container.index(where: { $0.raw != nil && predicate($0.raw!) }) {
            container.remove(at: index)
        }
    }
    
    public /// 移除所有元素
    mutating func removeAll() {
        container.removeAll()
    }
    
    public /// 垃圾清除
    /// - Note: 因为包含弱引用，所以有必要提供这个方法来做一些清理工作
    mutating func gc() {
        // 移除无效弱引用
        while let index = container.index(where: { $0.raw == nil }) {
            container.remove(at: index)
        }
    }
}

// MARK: - IEvent

/// https://github.com/apple/swift-evolution/blob/master/proposals/0077-operator-precedence.md
/// 定义新的操作运算符
precedencegroup WeakAdditionPrecedence {
    associativity: right
    lowerThan: AdditionPrecedence
}
infix operator +=? : WeakAdditionPrecedence

/// 与Notification类似，但有更强的协议约束。
/// 也可以参考一些事件多播的概念。
public protocol IEvent: IMulticast {
    
    /// 事件消费者容器类型。默认指定`WeakableContainer`
    associatedtype Container: IWeakableContainer = WeakableContainer<Handler>
    
    /// 元素容器。
    /// - Note: 虽然提供了该属性，但一般情况下，不要直接使用container操作元素。
    /// 建议通过IEvent接口方法如：`+=`、`+=?`、`-=`来进行操作，
    /// 或者通过IEvent相关扩展方法来进行操作。
    var container: Container { get set }
    
    /// 添加强引用
    static func +=(lhs: inout Self, rhs: Handler)
    
    /// 添加弱引用
    static func +=?(lhs: inout Self, rhs: Handler)
    
    /// 移除引用
    static func -=(lhs: inout Self, rhs: Handler)
    
    /// 移除所有事件元素
    mutating func clear()
    
    // 按条件移除引用目前不开放，感觉使用率不太高，而且操作风格和以上3种操作不太一致
}

/// 添加强引用
public func +=<T: IEvent>(lhs: inout T, rhs: T.Container.Element) {
    lhs.container.add(element: rhs)
}

/// 添加弱引用
public func +=?<T: IEvent>(lhs: inout T, rhs: T.Container.Element) {
    lhs.container.addWeak(element: rhs)
}

/// 移除引用
public func -=<T: IEvent>(lhs: inout T, rhs: T.Container.Element) {
    lhs.container.remove(element: rhs)
}

extension IEvent {
    
    /// 扩展：事件有效元素个数
    public var count: Int {
        return container.count
    }
    
    /// 扩展：移除所有事件元素
    public mutating func clear() {
        container.removeAll()
    }
    
    /// 扩展：判断元素是否存在
    ///
    /// - Parameter element: 元素
    /// - Returns: 是否存在
    public func contains(_ element: Container.Element) -> Bool {
        return container.contains(element: element)
    }
    
    /// 扩展：多播方法
    ///
    /// - Parameter thunk:消息转换程序（闭包）
    public func invoke(_ thunk: (Container.Element)->Void) {
        for handler in container.getIterator() {
            thunk(handler)
        }
    }
    
    /// 扩展：GC
    public mutating func gc() {
        container.gc()
    }
}


